var trace = function(x) {
	document.write('<pre>' + sprintf.apply(this, arguments) + '</pre>');
};


var Stream = function(str) {
	this.load(str);
};
Stream.prototype = {
	EOF: -1,
	load: function(str) {
		this.source = str;
		this.length = this.source.length;
		this.reset();
	},
	eof: function() {
		return this.char === this.EOF;
	},
	reset: function() {
		this.pos = 0;
	},
	getch: function() {
		if (this.pos >= this.length)
			return this.char = this.EOF;
		return this.char = this.source[this.pos++];
	}
};

var last_rule_id = 1;

var lex_rule = function(str, f) {
	var out = [];
	var i, max = str.length;
	for (i = 0; i < max; ++i)
		out[i] = {};
	//var action = function() { f(str) };
	var action = f;
	//action.toString = function() { return '[' + str + ']' };
	out[out.length] = { action: action };
	for (i = 0, max = out.length - 1; i < max; ++i)
		out[i][str[i]] = out[i + 1];
	return out[0];
};


var lex_merge = function(r1, r2) {
	//r2 = objoin({}, r2);
	
	if (r2.action && !r1.action) {
		r1.action = r2.action;
	}

	for (var i in r2) {
		if (i.length > 1 && i != 'any') continue;
		if (!r1[i]) {
			//r1[i] = objoin({}, r2[i]);
			r1[i] = r2[i];
			continue;
		}
		lex_merge(r1[i], r2[i]);
	}

	if (r2.default_action) {
		x = {};
		if (r1.default_action)
			x.d = r1.default_action;
		y = { d: r2.default_action };
		obcat(x, y);
		r1.default_action = x.d;
	}
	
	return r1;
};


var lex_run = function(initial, input) {
	var s, state = initial;
	var c;
	//console.dir(state);
	var i = 0, max = input.length;
	yytext = '';
	var d;
	var last_accepting_pos = 0, last_accepting_state = initial;
	var action;
	var x;
	var yytext_pos = 0;
	
	var cnt = 0;
	while (true) {
		if (cnt++ > 1000) { alert('hang up'); return; }
		
		if (i >= max) break;
		trace(input[i]);
		
		state = initial;
		while (s = state[input[i]]) {
			if (state.action) {
				last_accepting_state = state;
				last_accepting_pos = i;
			}
			++i;
			state = s;
		}
		if (!state.action) {
			state = last_accepting_state;
			i = last_accepting_pos + 1;
			//action = state.action;
		}
		yytext = input.substr(yytext_pos, i - yytext_pos);
		yytext_pos = i;
		
		if (state.action) {
			x = state.action();
			trace('action: ' + x);
		}
		
	}
};






